Skip to content

fix(config): apply ipv4_only IPv6 mode so TUN doesn't hijack the default IPv6 route - #140

Open
zZZTeJleTTy3uKZZz wants to merge 1 commit into
hiddify:mainfrom
zZZTeJleTTy3uKZZz:fix/ipv4-only-ipv6-mode
Open

fix(config): apply ipv4_only IPv6 mode so TUN doesn't hijack the default IPv6 route#140
zZZTeJleTTy3uKZZz wants to merge 1 commit into
hiddify:mainfrom
zZZTeJleTTy3uKZZz:fix/ipv4-only-ipv6-mode

Conversation

@zZZTeJleTTy3uKZZz

@zZZTeJleTTy3uKZZz zZZTeJleTTy3uKZZz commented Jul 15, 2026

Copy link
Copy Markdown

Problem

ipv6-mode: ipv4_only is parsed but never applied. Selecting "IPv4 only" has no effect on the generated sing-box config.

Root cause

In v2/config/builder.go, setInbound() derives IPv6 usage from isIPv6Supported() instead of the user's IPv6Mode. The code that would apply IPv6Mode is commented out (lines ~434-439 and ~459-469).

isIPv6Supported() only resolves the IPv6 loopback:

_, err := net.ResolveIPAddr("ip6", "::1")
return err == nil

which succeeds on virtually any host — even one with no global IPv6. So ipv6Enable is effectively always true: the TUN always receives an IPv6 address, auto_route captures the default IPv6 route, and inbounds bind to :: — regardless of the IPv4-only choice.

On hosts without global IPv6, browsers try AAAA first (Happy Eyeballs); packets enter the TUN but egress has no usable IPv6, so the TLS handshake is dropped (ERR_CONNECTION_CLOSED). Example: yandex.ru (routed .ru -> direct) fails over IPv6 while curl -4 succeeds. The only workaround is disabling IPv6 OS-wide.

Fix

ipv6Enable := isIPv6Supported() &&
    hopt.IPv6Mode != option.DomainStrategy(C.DomainStrategyIPv4Only)

Same conversion pattern already used in hiddify_option.go.

Verified (built and tested, not just theory)

Built this patch into hiddify-core.dll (from the v4.1.0 tree, Windows/amd64) and ran it under the installed Hiddify 4.1.1 on Windows 11, with ipv6-mode: ipv4_only and TUN enabled.

Before (stock core) — generated current-config.json:

"address": ["172.19.0.1/28", "fdfe:dcba:9876::1/126"],   // TUN takes an IPv6 address
"listen": "::"                                            // inbound binds IPv6

After (patched core) — same settings:

"address": "172.19.0.1/28",   // IPv4 only, as requested
"listen": "0.0.0.0"

Result: sites with AAAA records (e.g. yandex.ru) now load correctly over IPv4 through the TUN, with no OS-level IPv6 workaround needed.

Bonus: this also fixes failed to start background core on hosts where IPv6 is disabled OS-wide — the core no longer tries to bind :: or assign an IPv6 address to the TUN.

Notes

  • The same commented-out block also disables resolve-destination — worth restoring separately.
  • For hosts that do have global IPv6, a follow-up could also set dns.strategy: ipv4_only; getDNSServerOptions currently hardcodes prefer_ipv4, which does not drop AAAA.

Related: hiddify/hiddify-app#2172 (same symptom — same version, same ipv4_only, sites failing in VPN mode — reported without the IPv6 diagnosis).

…ult IPv6 route

The ipv6-mode: ipv4_only option was parsed but never applied. setInbound() used
isIPv6Supported() - which only resolves the IPv6 loopback and therefore succeeds
on virtually any host - instead of the user's IPv6Mode. As a result the TUN always
received an IPv6 address and auto_route captured the default IPv6 route, and
inbounds bound to ::, regardless of the IPv4-only choice.

On hosts without global IPv6 this black-holes every AAAA-first connection
(e.g. yandex.ru fails while curl -4 works).

Verified by building the patched core and running it under Hiddify 4.1.1: with
ipv4_only the TUN is now generated IPv4-only and AAAA sites load correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@zZZTeJleTTy3uKZZz
zZZTeJleTTy3uKZZz force-pushed the fix/ipv4-only-ipv6-mode branch from 061f87b to 275cde0 Compare July 16, 2026 11:46
@rug-lizi

rug-lizi commented Sep 9, 2026

Copy link
Copy Markdown

Evidence for hiddify/hiddify-core PR #140

Independent reproduction with Hiddify app 4.1.1 / released core v4.1.0 on Windows, plus a WPS Office desktop failure that is consistent with this change.

Deterministic configuration-only reproduction

This does not require a real subscription or a working server. It only builds configuration; it does not start TUN or change system networking.

profile.json (documentation-only address):

{"outbounds":[{"type":"socks","tag":"documentation-only","server":"192.0.2.1","server_port":1080}]}

options.json:

{"ipv6-mode":"ipv4_only","enable-tun":true,"remote-dns-address":"1.1.1.1","direct-dns-address":"1.1.1.1","mixed-port":12334,"mtu":1500,"tun-implementation":"system","region":"other"}

Run the core's build -d options.json -c profile.json -o built.json command. On my installed Windows package, I invoked the shipped hiddify-core.dll CLI export with this Python wrapper (paths supplied as arguments):

import ctypes, sys
dll = ctypes.CDLL(sys.argv[1])
cli = dll.parseCli
cli.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
cli.restype = ctypes.c_char_p
args = [b"HiddifyCli"] + [x.encode("utf-8") for x in sys.argv[2:]]
argv = (ctypes.c_char_p * len(args))(*args)
result = cli(len(args), argv)
if result:
    print(result.decode("utf-8", errors="replace"))

The released DLL actually generated these inbounds (excerpt):

[
  {"type":"tun","tag":"tun-in","address":["172.19.0.1/28","fdfe:dcba:9876::1/126"],"auto_route":true},
  {"type":"mixed","tag":"mixed-in::1","listen":"::1"},
  {"type":"mixed","tag":"mixed-in127.0.0.1","listen":"127.0.0.1"}
]

Expected with explicit ipv4_only: no IPv6 TUN address or IPv6 mixed listener. The problem is also visible in main at db74dfc257d5becb4b4e9dbc7257a3dcdde20692: setInbound() only uses isIPv6Supported(), which resolves ::1; the user's IPv6 mode is not consulted. A loopback parse is not evidence of usable global IPv6.

Real application evidence

  • TUN/system stack, China-region direct routing, MTU 1500; physical network had IPv4 but no global IPv6 address. WPS Office 12.1.0.28043 cloud documents failed to load.
  • Hiddify logged direct outbound connections to WPS IPv6 destinations failing with An invalid argument was supplied (destination addresses and connection identifiers omitted).
  • Clearing DNS and temporarily preferring IPv4 did not fix the desktop failure; the temporary prefix-precedence change was reverted.
  • Disabling ms_tcpip6 only on the Hiddify tun0 adapter restored WPS cloud listing/refresh. A subsequent test with the other proxy application fully exited also worked. This was a local workaround, not an OS-wide IPv6 disable.
  • Disconnect/reconnect recreated/enabled IPv6 on tun0, so the one-time workaround did not persist. A local compatibility task now reapplies that adapter-only workaround. It should not be necessary once the core honors the selected mode.

There is a related Windows report at hiddify/hiddify-app#2307, closed by github-actions[bot] on 2026-09-08. The configuration-only reproduction above provides evidence independently of the affected website or application.

Scope and suggested regression coverage

I verified the released-core failure and the adapter-only workaround, not a build of this PR. No claim here that this PR has passed Windows integration tests on my machine.

The guard in this PR addresses the configuration error reproduced above. A regression test for setInbound() would be valuable: with EnableTun=true and IPv6Mode=ipv4_only, assert no IPv6 TUN prefix and no IPv6 mixed listener; separately confirm that the default/non-IPv4-only modes retain their intended behavior. DNS strategy / AAAA handling can remain a separate follow-up.

An additional ChatGPT desktop failure during this investigation came from a local stale .env pointing at a stopped proxy. It was fixed separately and is not attributed to this Hiddify bug. No credentials, subscription contents, personal documents, or real server addresses are included in this report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants